/*
* World.cpp
*
*  Created on: 2009/03/04
*      Author: Yongshik Cho
*/

#include "World.h"
#include "core/Math3D.h"
#include "WorldManager.h"


World::World(void)
{
	mWorld = 0;
	mWind = Vector2D(0.f, 0.f);

	b2AABB worldAABB;
	worldAABB.lowerBound.Set(0.0f, 0.0f);
	worldAABB.upperBound.Set(800.0f, 610.0f);

	// Define the gravity vector.
	mGravity = WorldManager::_DEFAULT_GRAVITY;
	b2Vec2 gravity(mGravity.x, mGravity.y);

	// Do we want to let bodies sleep?
	bool doSleep = true;

	// Construct a world object, which will hold and simulate the rigid bodies.
	mWorld = new b2World(worldAABB, gravity, doSleep);

	// Define the ground body.
	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0.0f, 605.f);

	// Call the body factory which allocates memory for the ground body
	// from a pool and creates the ground box shape (also from a pool).
	// The body is also added to the world.
	b2Body* groundBody = mWorld->CreateBody(&groundBodyDef);

	// Define the ground box shape.
	b2PolygonDef groundShapeDef;

	// The extents are the half-widths of the box.
	groundShapeDef.SetAsBox(800.0f, 5.0f);

	// Add the ground shape to the ground body.
	groundBody->CreateShape(&groundShapeDef);
}

World::~World(void)
{
	//clear all world bodies
	mBodies.clear();

	//delete world
	SAFE_DELETE(mWorld);
}

void World::tick(const uint32 elapsed)
{
	if(!mWorld || !elapsed)
		return;

	float timeStep = 1.f / (float)elapsed;
	int32 iterations = 10;

	mWorld->Step(timeStep, iterations);

	for(BodyType::iterator it=mBodies.begin();it!=mBodies.end();++it)
	{
		RenderObject* obj = (*it).first;
		b2Body* body = (*it).second;

		if(obj && body)
		{
			float32 angle = body->GetAngle();
			b2Vec2 pos = body->GetPosition();

			obj->setPosition(Vector2D(pos.x, pos.y));
			obj->setRotation(Math::RadiansToDegrees(angle));
		}
	}
}

void World::setGravity(const Vector2D& v)
{
	if(!mWorld)
		return;

	mGravity = v;
	mWorld->SetGravity(b2Vec2(mGravity.x + mWind.x, mGravity.y + mWind.y));
}

void World::setWind(const Vector2D& v)
{
	if(!mWorld)
		return;

	mWind = v;
	mWorld->SetGravity(b2Vec2(mGravity.x + mWind.x, mGravity.y + mWind.y));
}

void World::addBody(RenderObject* obj)
{
	if(!mWorld || !obj)
		return;

	// Define the dynamic body. We set its position and call the body factory.
	Vector2D pos = obj->getPosition();
	Vector2D size = obj->getSize();

	b2BodyDef bodyDef;
	bodyDef.position.Set(pos.x, pos.y);
	bodyDef.angle = Math::DegreesToRadians(obj->getRotation());
	b2Body* body = mWorld->CreateBody(&bodyDef);

	// Define another box shape for our dynamic body.
	b2PolygonDef shapeDef;
	shapeDef.SetAsBox(size.x/2.f, size.y/2.f);

	// Set the box density to be non-zero, so it will be dynamic.
	shapeDef.density = 1.0f;

	// Override the default friction.
	shapeDef.friction = 0.3f;

	// Add the shape to the body.
	body->CreateShape(&shapeDef);
	//body->SetAngularVelocity(0.3f);

	// Now tell the dynamic body to compute it's mass properties base
	// on its shape.
	body->SetMassFromShapes();

	//add body to the list
	mBodies[obj] = body;
}

b2Body* World::getBody(RenderObject* obj)
{
	if(!obj)
		return 0;

	return mBodies[obj];
}